/*
 * Project Name spring-boot-tools
 * File Name VersionRequestCondition
 * Package Name com.huxiaosu.tools.common.version
 * Create Time 2024/7/16
 * Create by name：liujie
 */
package com.huxiaosu.tools.common.version;

import com.huxiaosu.tools.common.annotation.TerminalVersion;
import com.huxiaosu.tools.common.constant.Terminals;
import com.huxiaosu.tools.common.constant.VersionOperator;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition;

import java.util.*;

/**
 * Description
 *
 * @author liujie
 * @date 2024/7/16 09:57
 */
public class VersionRequestCondition extends AbstractRequestCondition<VersionRequestCondition> {
    // consts
    public static final char SEP = ',';

    private final Set<TerminalVersionExpression> expressions;

    protected VersionRequestCondition(Set<TerminalVersionExpression> expressions) {
        this.expressions = expressions;
    }

    public VersionRequestCondition(TerminalVersion[] terminalVersions) {
        expressions = Collections.unmodifiableSet(parseByTerminalVersion(terminalVersions));
    }

    private static Set<TerminalVersionExpression> parseByTerminalVersion(TerminalVersion[] terminalVersions) {
        Set<TerminalVersionExpression> expressions = new LinkedHashSet<>();
        for (TerminalVersion terminalVersion : terminalVersions) {
            Terminals[] terminals = terminalVersion.terminals();
            int[] all = Arrays.stream(terminals).mapToInt(Terminals::getType).toArray();
            expressions.add(new TerminalVersionExpression(all, terminalVersion.version(), terminalVersion.op()));
        }
        return expressions;
    }

    @Override
    protected Collection<?> getContent() {
        return this.expressions;
    }

    // 同param,一项不满足，即匹配失败
    @Override
    protected String getToStringInfix() {
        return " && ";
    }

    @Override
    public VersionRequestCondition combine(VersionRequestCondition other) {
        Set<TerminalVersionExpression> set = new LinkedHashSet<TerminalVersionExpression>(this.expressions);
        set.addAll(other.expressions);
        return new VersionRequestCondition(set);
    }

    @Override
    public VersionRequestCondition getMatchingCondition(HttpServletRequest request) {
        for (TerminalVersionExpression expression : expressions) {
            // 同param condition,任意一个失败则失败
            if (!expression.match(request)) {
                return null;
            }
        }
        return this;
    }

    @Override
    public int compareTo(VersionRequestCondition other, HttpServletRequest request) {
        return 0;
    }

    static class TerminalVersionExpression {

        public static final String HEADER_VERSION = "cv";

        public static final String HEADER_TERMINAL = "terminal";

        private int[] terminals;

        private final String version;

        private final VersionOperator operator;

        public int[] getTerminals() {
            return terminals;
        }

        public void setTerminals(int[] terminals) {
            this.terminals = terminals;
        }

        public String getVersion() {
            return version;
        }

        public TerminalVersionExpression(int[] terminals, String version, VersionOperator operator) {
            Arrays.sort(terminals);
            if (StringUtils.isNotBlank(version) && operator == VersionOperator.NIL) {
                throw new IllegalArgumentException("opetator cant be nil when version is existing...");
            }
            this.terminals = terminals;
            this.version = version;
            this.operator = operator;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (terminals != null && terminals.length != 0) {
                builder.append(StringUtils.join(terminals, SEP, 0, terminals.length));
            }
            else {
                builder.append("*");
            }
            builder.append(operator.getCode());
            if (StringUtils.isNotBlank(version)) {
                builder.append(version);
            }
            return builder.toString();
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj instanceof TerminalVersionExpression) {
                // 暂定最终的表达式结果一致确定唯一性，后期有需要调整
                return this.toString().equalsIgnoreCase(obj.toString());
            }
            return false;
        }

        // 同上，如果需要复杂判定，则需要自己实现hashcode
        @Override
        public int hashCode() {
            return this.toString().hashCode();
        }

        public final boolean match(HttpServletRequest request) {
            // 匹配客户端类型
            if (this.terminals != null && this.terminals.length > 0) {
                int terminal = getTerminal(request);
                int i = Arrays.binarySearch(terminals, terminal);
                // 未找到则匹配失败
                if (i < 0) {
                    return false;
                }
            }
            if (this.operator != null && this.operator != VersionOperator.NIL) {
                String clientVersion = getVersion(request);
                String checkVersion = getVersion();
                if (StringUtils.isBlank(clientVersion)) {
                    // 尽量保证快速失败
                    return false;
                }
                int i = clientVersion.compareToIgnoreCase(checkVersion);
                switch (operator) {
                    case GT -> {
                        return i > 0;
                    }
                    case GTE -> {
                        return i >= 0;
                    }
                    case LT -> {
                        return i < 0;
                    }
                    case LTE -> {
                        return i <= 0;
                    }
                    case EQ -> {
                        return i == 0;
                    }
                    case NE -> {
                        return i != 0;
                    }
                    default -> {
                    }
                }
            }
            return true;
        }

        private int getTerminal(HttpServletRequest request) {
            String value = request.getHeader(HEADER_TERMINAL);
            try {
                return Integer.parseInt(value);
            }
            catch (Exception e) {
                return -1;
            }
        }

        private String getVersion(HttpServletRequest request) {
            return request.getHeader(HEADER_VERSION);
        }

    }
}